#include "Camera.h"

using namespace std;

cCamera::cCamera() {
    this->m_pCurrentRotY = 0.0;
    this->m_pCurrentRotXZ = 0.0;

    this->m_pPos.x = 0.0;
    this->m_pPos.y = 0.0;
    this->m_pPos.z = 0.0;

    this->m_pLookAt.x = 0.0;
    this->m_pLookAt.y = 0.0;
    this->m_pLookAt.z = 0.0;

    this->m_pUpVector.x = 0.0;
    this->m_pUpVector.y = 1.0;
    this->m_pUpVector.z = 0.0;

    this->m_pCurrentRotation = 0.0;
}

cCamera::~cCamera() {
}

void cCamera::update(float elapsedTime) {
    float forwardMovement = 0.0;
    float strafeMovement = 0.0;
    float jumpMovement = 0.0;
    float xzRotation = 0.0;
    float yRotation = 0.0;
    int moving = false;
    int rotating = false;

//    //Analizar input de teclado
//
//    // ADELANTE Y ATRAS
//    if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_W)) {
//        forwardMovement = DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    }
//    else if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_S)) {
//        forwardMovement = -DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    }
//
//    // IZQUIERDA Y DERECHA
//    if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_A)) {
//        strafeMovement = DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    } else if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_D)) {
//        strafeMovement = -DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    }
//
//    // ARRIBA Y ABAJO
//    if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_SPACE)) {
//        jumpMovement = DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    }
//    if (cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_LCTRL)) {
//        jumpMovement = -DEFAULT_MOVEMENT_SPEED;
//        moving = true;
//    }
//
//    //Analizar mouse
//    if (cInputLayer::GetInputLayer()->buttonDown(INPUT_MOUSE_BUTTON_LEFT)) {
//        int eventMouseX, eventMouseY;
//        glfwGetMousePos(&eventMouseX, &eventMouseY);
//        yRotation = -cInputLayer::GetInputLayer()->xPosRelative * DEFAULT_ROTATION_ANGLE;
//        xzRotation = -cInputLayer::GetInputLayer()->yPosRelative * DEFAULT_ROTATION_ANGLE;
//        rotating = true;
//    }
//
//    //Acumular rotacion
//    this->m_pCurrentRotY += yRotation * elapsedTime;
//    this->m_pCurrentRotXZ += xzRotation * elapsedTime;
//
//    //Clamp de rotacion XZ entre [-PI/2, PI/2]
//    this->m_pCurrentRotXZ = this->m_pCurrentRotXZ > M_PI_2 ? M_PI_2 : this->m_pCurrentRotXZ;
//    this->m_pCurrentRotXZ = this->m_pCurrentRotXZ < -M_PI_2 ? -M_PI_2 : this->m_pCurrentRotXZ;
//
//    //Wrap de rotacion Y entre [0, 2PI]
//    this->m_pCurrentRotY = this->m_pCurrentRotY > M_PI ? this->m_pCurrentRotY - (2 * M_PI) : this->m_pCurrentRotY;
//    this->m_pCurrentRotY = this->m_pCurrentRotY < 0 ? (2 * M_PI) + this->m_pCurrentRotY : this->m_pCurrentRotY;
//
//    //Obtener angulos de direccion segun rotacion en Y y en XZ
//    float dirX = sin(this->m_pCurrentRotY);
//    float dirZ = cos(this->m_pCurrentRotY);
//    float dirY = sin(this->m_pCurrentRotXZ);
//    
//    //Direcciones de movimiento
//    Vector3 movementDir;
//    movementDir.x = dirX;
//    movementDir.y = 0;
//    movementDir.z = dirZ;
//    
//    
//    Vector3 strafeDir;
//    strafeDir.x = 0;
//    strafeDir.y = 0;
//    strafeDir.z = 0;
//    strafeDir = cCamera::cross(m_pUpVector, movementDir);
//
//    //Movimiento adelante-atras
//    cCamera::scale(&movementDir, forwardMovement /* * elapsedTime */);
//
//    //Movimiento strafe
//    cCamera::scale(&strafeDir, strafeMovement /* * elapsedTime */);
//
//    //Sumar movimiento
//    cCamera::add(&this->m_pPos, movementDir);
//    cCamera::add(&this->m_pPos, strafeDir);
//
//    this->m_pPos.y += jumpMovement;
//
//    //Hacia donde mirar
//    Vector3 lookAtDir;
//    lookAtDir.x = dirX;
//    lookAtDir.y = dirY;
//    lookAtDir.z = dirZ;
//    cCamera::normalize(&lookAtDir);
//
//    // Set
//    this->m_pLookAt.x = this->m_pPos.x;
//    this->m_pLookAt.y = this->m_pPos.y;
//    this->m_pLookAt.z = this->m_pPos.z;
//    
//    cCamera::add(&this->m_pLookAt, lookAtDir);
    
    // ************************************************************* //
    // ************************************************************* //
    // ************************************************************* //

        float movement = 0.0;
        float rotation = 0.0;
        float jump = 0.0;
        
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_W)){
            movement = DEFAULT_MOVEMENT_SPEED;
        }
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_S)){
            movement = -DEFAULT_MOVEMENT_SPEED;
        }
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_A)){
            rotation = DEFAULT_ROTATION_ANGLE;
        }
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_D)){
            rotation = -DEFAULT_ROTATION_ANGLE;
        }
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_SPACE)){
            jump = DEFAULT_MOVEMENT_SPEED;
        }
        if(cInputLayer::GetInputLayer()->keyDown(INPUT_KEY_LCTRL)){
            jump = -DEFAULT_MOVEMENT_SPEED;
        }
        
        rotation = (rotation * M_PI) / 180; // Se pasa a radianes
        this->m_pCurrentRotation += rotation;
        
        // Movimiento en eje y
        this->m_pPos.y += jump;
        this->m_pLookAt.y = this->m_pPos.y;
        
        // Movimiento en eje x y z
        this->m_pPos.x += sin(this->m_pCurrentRotation) * movement;
        this->m_pPos.z += cos(this->m_pCurrentRotation) * movement;
        
        // Look at
        this->m_pLookAt.x = this->m_pPos.x + sin(this->m_pCurrentRotation);
        this->m_pLookAt.z = this->m_pPos.z + cos(this->m_pCurrentRotation);
}

void cCamera::render() {
    cGraphicsLayer::GetGraphics()->matrixView = glm::lookAt(
        glm::vec3(this->m_pPos.x, this->m_pPos.y, this->m_pPos.z),
        glm::vec3(this->m_pLookAt.x, this->m_pLookAt.y, this->m_pLookAt.z),
        glm::vec3(this->m_pUpVector.x, this->m_pUpVector.y, this->m_pUpVector.z)
    );
}

// OPERADORES MATEMATICOS PARA VECTORES

Vector3 cCamera::cross(Vector3 vec, Vector3 vec2) {
    Vector3 des;
    int x = vec.x, y = vec.y, z = vec.z,
            x2 = vec2.x, y2 = vec2.y, z2 = vec.z;

    des.x = y * z2 - z * y2;
    des.y = z * x2 - x * z2;
    des.z = x * y2 - y * x2;
    return des;
};

void cCamera::scale(Vector3 *vec, float val) {
    vec->x = vec->x * val;
    vec->y = vec->y * val;
    vec->z = vec->z * val;
};

void cCamera::add(Vector3 *vec, Vector3 vec2) {
    vec->x = vec->x + vec2.x;
    vec->y = vec->y + vec2.y;
    vec->z = vec->z + vec2.z;
};

void cCamera::normalize(Vector3 *vec) {
    int x = vec->x, y = vec->y, z = vec->z,
            len = sqrt(x * x + y * y + z * z);

    if (!len) {
        vec->x = 0;
        vec->y = 0;
        vec->z = 0;
    }
    else if (len == 1) {
        vec->x = x;
        vec->y = y;
        vec->z = z;
    } else {
        len = 1 / len;
        vec->x = x * len;
        vec->y = y * len;
        vec->z = z * len;
    }
};